package com.gitee.wsl.security.encrypt.sample

import kotlin.jvm.JvmOverloads

/**
 * RotN（rotate by N places），回转N位密码，是一种简易的替换式密码，也是过去在古罗马开发的凯撒加密的一种变体。<br></br>
 * 代码来自：https://github.com/orclight/jencrypt
 *
 * @author looly,shuzhilong
 * @since 4.4.1
 */
object Rot {
    private const val aCHAR = 'a'
    private const val zCHAR = 'z'
    private const val ACHAR = 'A'
    private const val ZCHAR = 'Z'
    private const val CHAR0 = '0'
    private const val CHAR9 = '9'

    /**
     * Rot-13编码
     *
     * @param message 被编码的消息
     * @param isEncodeNumber 是否编码数字
     * @return 编码后的字符串
     */
    /**
     * Rot-13编码，同时编码数字
     *
     * @param message 被编码的消息
     * @return 编码后的字符串
     */
    @JvmOverloads
    fun encode13(message: String, isEncodeNumber: Boolean = true): String {
        return encode(message, 13, isEncodeNumber)
    }

    /**
     * RotN编码
     *
     * @param message 被编码的消息
     * @param offset 位移，常用位移13
     * @param isEncodeNumber 是否编码数字
     * @return 编码后的字符串
     */
    fun encode(message: String, offset: Int, isEncodeNumber: Boolean): String {
        val len = message.length
        val chars = CharArray(len)

        for (i in 0..<len) {
            chars[i] = encodeChar(message[i], offset, isEncodeNumber)
        }
        return chars.concatToString()
    }

    /**
     * Rot-13解码
     *
     * @param rot 被解码的消息密文
     * @param isDecodeNumber 是否解码数字
     * @return 解码后的字符串
     */
    /**
     * Rot-13解码，同时解码数字
     *
     * @param rot 被解码的消息密文
     * @return 解码后的字符串
     */
    @JvmOverloads
    fun decode13(rot: String, isDecodeNumber: Boolean = true): String {
        return decode(rot, 13, isDecodeNumber)
    }

    /**
     * RotN解码
     *
     * @param rot 被解码的消息密文
     * @param offset 位移，常用位移13
     * @param isDecodeNumber 是否解码数字
     * @return 解码后的字符串
     */
    fun decode(rot: String, offset: Int, isDecodeNumber: Boolean): String {
        //Assert.notNull(rot, "rot must not be null")
        val len = rot.length
        val chars = CharArray(len)

        for (i in 0..<len) {
            chars[i] = decodeChar(rot[i], offset, isDecodeNumber)
        }
        return chars.concatToString()
    }

    // ------------------------------------------------------------------------------------------ Private method start
    /**
     * 解码字符
     *
     * @param c 字符
     * @param offset 位移
     * @param isDecodeNumber 是否解码数字
     * @return 解码后的字符串
     */
    private fun encodeChar(c: Char, offset: Int, isDecodeNumber: Boolean): Char {
        var c = c
        if (isDecodeNumber) {
            if (c >= CHAR0 && c <= CHAR9) {
                c -= CHAR0.code
                c = ((c.code + offset) % 10).toChar()
                c += CHAR0.code
            }
        }

        // A == 65, Z == 90
        if (c >= ACHAR && c <= ZCHAR) {
            c -= ACHAR.code
            c = ((c.code + offset) % 26).toChar()
            c += ACHAR.code
        } else if (c >= aCHAR && c <= zCHAR) {
            c -= aCHAR.code
            c = ((c.code + offset) % 26).toChar()
            c += aCHAR.code
        }
        return c
    }

    /**
     * 编码字符
     *
     * @param c 字符
     * @param offset 位移
     * @param isDecodeNumber 是否编码数字
     * @return 编码后的字符串
     */
    private fun decodeChar(c: Char, offset: Int, isDecodeNumber: Boolean): Char {
        var temp = c.code
        // if converting numbers is enabled
        if (isDecodeNumber) {
            if (temp >= CHAR0.code && temp <= CHAR9.code) {
                temp -= CHAR0.code
                temp = temp - offset
                while (temp < 0) {
                    temp += 10
                }
                temp += CHAR0.code
            }
        }

        // A == 65, Z == 90
        if (temp >= ACHAR.code && temp <= ZCHAR.code) {
            temp -= ACHAR.code

            temp = temp - offset
            while (temp < 0) {
                temp = 26 + temp
            }
            temp += ACHAR.code
        } else if (temp >= aCHAR.code && temp <= zCHAR.code) {
            temp -= aCHAR.code

            temp = temp - offset
            if (temp < 0) temp = 26 + temp

            temp += aCHAR.code
        }
        return temp.toChar()
    } // ------------------------------------------------------------------------------------------ Private method end
}